package com.jrapider.designer.service;

import com.jrapider.om.SysEntity;
import com.jrapider.om.SysEntityField;
import com.jrapider.om.SysPage;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Component
public class FreemarkService {
    Configuration configuration;
    private File templatePath;
    private File outputPath;

    @Value("${designer.tempalte-path:codeTemplates}")
    private String TEMPLATE_PATH;
    @Value("${designer.output-path:code}")
    private String OUTPUT_PATH;
    @Value("${designer.base-package:com.jrapider.demo}")
    private String basePackage;
    @Value("${designer.backup:true}")
    private boolean backup;
    @Value("${designer.project-tempalte-path:projectTemplates}")
    private String PROJECT_TEMPLATE_PATH;
    @Value("${designer.project-output-path:project}")
    private String PROJECT_OUTPUT_PATH;


    public void genCode(SysEntity sysEntity, SysPage sysPage, List<SysEntityField> fieldList) throws Exception {
        try {
            templatePath = new File(TEMPLATE_PATH);
            outputPath = new File(OUTPUT_PATH);
            if (!templatePath.exists()) {
                templatePath.mkdirs();
            }
            if (!outputPath.exists()) {
                outputPath.mkdirs();
            }
            configuration = new Configuration(Configuration.VERSION_2_3_28);
            configuration.setDirectoryForTemplateLoading(templatePath);
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("entity", sysEntity);
            dataMap.put("page", sysPage);
            dataMap.put("fields", fieldList);
            dataMap.put("entityName", StringUtils.capitalize(sysEntity.getEntityName()));
            dataMap.put("module", sysEntity.getModule());
            dataMap.put("basePackage", basePackage);
            genFile(templatePath, outputPath, dataMap);

        } catch (IOException e) {
            throw new RuntimeException("代码生成器模版文件初始化失败", e);
        }


    }

    private void genFile(File templateFolder, File outputFolder, Map<String, Object> dataMap) throws IOException, TemplateException {
        File[] files = templateFolder.listFiles();
        for (File template : files) {
            if (template.isDirectory()) {
                String fileName = getTemplateName(template.getName(), dataMap);
                File output;
                if (!"".equals(fileName)) {
                    String folderName = fileName.replaceAll("\\.", File.separator);
                    output = new File(outputFolder, folderName);
                    output.mkdirs();
                } else {
                    output = outputFolder;
                }
                genFile(template, output, dataMap);
            } else if (!template.getName().endsWith(".ftl")) {
                FileCopyUtils.copy(template, new File(outputFolder, template.getName()));
            } else {
                Template templateMaker = configuration.getTemplate(template.getAbsolutePath().replace(templatePath.getAbsolutePath(), ""));
                //忽略代码生成的模版文件
                if (template.getName().startsWith("ignore.")) {
                    continue;
                }

                //剔除ftl后缀，还原真实文件名
                String fileName = template.getName().substring(0, template.getName().length() - 4);

                fileName = getTemplateName(fileName, dataMap);
                //@开头的模版文件时强制覆盖，非@开头，如果存在，不覆盖
                boolean needOverride = false;
                if (fileName.startsWith("@")) {
                    needOverride = true;
                    fileName = fileName.replace("@", "");
                }
                File output = new File(outputFolder, fileName);

                if (needOverride && output.exists()) {
                    if (backup) {
                        FileCopyUtils.copy(output, new File(outputFolder, output.getName() + "." + System.currentTimeMillis()));
                    }
                    output.delete();
                } else if (output.exists()) {
                    continue;
                }
                output.createNewFile();
                try (Writer out = new FileWriter(output)) {
                    templateMaker.process(dataMap, out);
                }
            }
        }

    }

    private String getTemplateName(String fileName, Map<String, Object> dataMap) {
        if (fileName == null) {
            return "";
        }
        Set<String> keys = dataMap.keySet();
        for (String key : keys) {

            fileName = fileName.replaceAll("\\$\\{" + key + "\\}", dataMap.get(key) != null ? dataMap.get(key).toString() : "");
        }
        return fileName;
    }


    public void buildProject(String projectName, String group) {
        try {
            templatePath = new File(PROJECT_TEMPLATE_PATH);
            outputPath = new File(PROJECT_OUTPUT_PATH+File.separator+projectName);
            FileSystemUtils.deleteRecursively(outputPath);
            if (!templatePath.exists()) {
                templatePath.mkdirs();
            }
            if (!outputPath.exists()) {
                outputPath.mkdirs();
            }
            configuration = new Configuration(Configuration.VERSION_2_3_28);
            configuration.setDirectoryForTemplateLoading(templatePath);
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("projectName", projectName);
            dataMap.put("group", group);
            genFile(templatePath, outputPath, dataMap);

        } catch (IOException e) {
            throw new RuntimeException("代码生成器模版文件初始化失败", e);
        } catch (TemplateException e) {
            throw new RuntimeException("代码生成失败", e);
        }
    }
}
